﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace Common.Net.Utilities
{
    public static class EnumerableHelper
    {
        public static IEnumerable<T> ToEnumerable<T>(this T item)
        {
            yield return item;
        }

        public static IEnumerable<T> ToEnumerable<T>(params T[] values)
        {
            foreach (var value in values)
                yield return value;
        }

        public static IEnumerable<T> WithTail<T>(this T head, IEnumerable<T> tail)
        {
            return head.ToEnumerable().WithTail(tail);
        }

        public static IEnumerable<T> WithTail<T>(this T head, T tail)
        {
            return head.ToEnumerable().WithTail(tail);
        }

        public static IEnumerable<T> WithTail<T>(this IEnumerable<T> head, T tail)
        {
            return head.WithTail(tail.ToEnumerable());
        }

        public static IEnumerable<T> WithTail<T>(this IEnumerable<T> head, IEnumerable<T> tail)
        {
            foreach (var item in head)
                yield return item;

            foreach (var item in tail)
                yield return item;
        }

        public static IEnumerable<TResult> Traverse<TObj, TResult>(this IEnumerable<TObj> objects, Func<TObj, IEnumerable<TResult>> innerAccessor)
        {
            foreach (var obj in objects)
                foreach (var value in innerAccessor(obj))
                    yield return value;
        }

        public static T SingleOrDefault<T>(this IEnumerable<T> source, Func<T, bool> predicate, T defaultValue)
        {
            T result = default(T);
            bool found = false;

            foreach (var value in source)
            {
                if (predicate(value))
                {
                    if (found)
                        throw ExceptionFactory.InvalidOperation("Several matches were found");
                    else
                    {
                        result = value;
                        found = true;
                    }
                }
            }

            return found ? result : defaultValue;
        }

        public abstract class SequenceComparisonResult
        {
            // Equal, different number of items, different item at positioin
            public abstract bool AreEqual { get; }

            public class EqualSequencesVariant : SequenceComparisonResult
            {
                public override bool AreEqual
                {
                    get { return true; }
                }
            }

            public class NotEqualSequencesVariant : SequenceComparisonResult
            {
                public override bool AreEqual
                {
                    get { return false; }
                }
            }
        }

        public static SequenceComparisonResult AreSequencesEqual<T>(IEnumerable<T> left, IEnumerable<T> right)
        {
            // TODO: Make this code smarter: there is no need to create lists
            // TODO: This method should return more specific results - information about inequality
            List<T> leftList = left.ToList();
            List<T> rightList = right.ToList();

            if (leftList.Count != rightList.Count)
                return new SequenceComparisonResult.NotEqualSequencesVariant();

            for (int i = 0; i < leftList.Count; i++)
            {
                if (!EqualityComparer<T>.Default.Equals(leftList[i], rightList[i]))
                    return new SequenceComparisonResult.NotEqualSequencesVariant();
            }

            return new SequenceComparisonResult.EqualSequencesVariant();
        }
    }

    public static class EnumerableHelper<T>
    {
        public static IEnumerable<T> Empty
        {
            get { yield break; }
        }
    }
}
